//: Playground - noun: a place where people can play

import UIKit
//:   构造过程: 通过自定义构造器对类 结构体 枚举的实例进行初始化操作
// 构造器类似于不带参数的构造方法
struct Weather {
    var temperature: Double            // 也可以直接设置默认属性
    init() {                           // 不带参数的构造器:init() {构造过程}
        temperature = 26.0             // 在构造器中给存储属性赋值
    }
}
var w = Weather()                      // 初始化过程会调用构造方法
print(w.temperature)                   // 26.0


//:  自定义构造过程
struct Point {
    var x = 2.0, y = 4.0               // 原始位置
    let z: Double                      // 定义一个常量
    // pointX参数内部名称在构造方法内部使用,外部名称在使用构造方法的时候使用
    init(fromPointX pointX: Double, _ pointY: Double) { // _ 表示省略外部参数
        x += pointX
        y += pointY
        z = pointX                     // 常量在构造过程中被赋值后不可更改
    }
    // 如果没有提供外部名称,swift会自动生成一个和内部名称相同的外部名
}
let point = Point(fromPointX:3.0,3.0)
print("x:\(point.x)  y:\(point.y) z:\(point.z)")


//:  默认构造器:如果结构体和类的所有属性都有默认值,同时没有自定义构造器,swift会提供一个默认构造器
class List {
    var name : String?                 // 可选类型默认设置为nil
    var quantity = 1
    var purchased = false
}
var item = List()
print("\(String(describing: item.name)) \(item.quantity) \(item.purchased)") // nil 1 false
struct Size {                          // 既没有提供构造器有没有设置初始值
    var width = 0.0, height = 0.0
}
var size1 = Size(width: 3.0, height: 4.0) // 结构体可以获得一个逐一构造器


//:  值类型的构造代理:构造器调用其他构造器来完成部分实例化
struct RectSize {
    var width = 0.0, height = 0.0
}
struct RectPoint {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = RectPoint()
    var size = RectSize()
    init() {}                          // 没有执行任何构造过程,返回一个Rect实例
    init(origin: RectPoint, size: RectSize) {
        self.origin = origin
        self.size = size
    }
    init(center: RectPoint, size: RectSize) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: RectPoint(x: originX, y: originY), size: size)
    }
}
let basicRect = Rect()
let originRect = Rect(origin:RectPoint(x:2.0,y:2.0),size:RectSize(width:
    2.0, height: 2.0))
let centerRect = Rect(center: RectPoint(x: 3.0, y: 3.0), size: RectSize(width:
    2.0, height: 2.0))                 // 先计算origin坐标,再在内部调用其他构造器进行赋值

//:  类的构造: 类的所有属性包括继承父类的都必须在构造过程中设置初始值.类有两种构造器指定构造器和遍历构造器
//init(参数) {                            // 指定构造器语法,初始化类中提供的所有属性,
//    // 初始语句                         // 会调用父类构造器来实现父类的初始化,
//}                                      // 所有类都必须拥有父类构造器,但也能通过继承获得.
//
//convenience init(参数) {                // 便利构造器语法,init前加关键字convenience
//    // 初始语句                         // 比较次要的,一般用来使类的构造过程更清晰明了
//}

// 子类的指定构造器会调用父类的指定构造器,便利构造器必须调用该类的指定构造器


class Vehicle {
    var wheels = 1                     // 定义一个提供默认值的存储属性
    var description: String {          // 返回String的计算属性
        return "有\(wheels)个轮子"
    }
}
let vehicle = Vehicle()                // 系统会提供指定构造器创建wheels为0的实例属性
print(vehicle.description)             // 有1个轮子


class Bicycle: Vehicle {               // 继承Vehicle的子类
    override init() {                  // 构造器和父类相同要加override,需要重写父类构造器
        super.init()                   // 初始化父类构造器
        wheels = 2                     // 替换新值,但不能修改继承来的常量属性
    }
}
let bicycle = Bicycle()
print(bicycle.description)             // 有2个轮子


//:  指定构造器和遍历构造器的实践
class Food {
    var name: String
    init(name: String) {               // 指定构造器
        self.name = name
    }
    convenience init() {               // 便利构造器,横向调用指定构造器
        self.init(name: "名字未知")
    }
}
let nameMeat = Food(name: "Becon")
print(nameMeat.name)                   // Becon
let unknownMeat = Food()               // 会调用没有参数的便利构造器
print(unknownMeat.name)                // 名字未知


class Recepe: Food {
    var quantity: Int
    init(name: String, quantity: Int) { // 指定构造器
        self.quantity = quantity       // 初始化属性
        super.init(name: name)         // 初始化父类属性
    }
    override convenience init(name: String) { // 遍历构造器重写了父类的指定构造器
        self.init(name: name, quantity: 1) // 调用指定构造器,quantity为默认值1
    }
}

let recepe1 = Recepe()
print("\(recepe1.name),\(recepe1.quantity)")  // 名字未知,1
let recepe2 = Recepe(name: "Beef")
print("\(recepe2.name),\(recepe2.quantity)")  // Beef,1
let recepe3 = Recepe(name: "Egg", quantity: 3)
print("\(recepe3.name),\(recepe3.quantity)")  // Egg,3


//:  可失败构造器:类,结构体或枚举在定义的时候有可能会失败,可以定义可失败构造器
struct Animal {
    let species: String
    init?(species: String) {           // 语法:init?(参数) {}
        if species.isEmpty { return nil }
        self.species = species
    }
}
let cat = Animal(species: "Cat")       // 失败构造器会创建一个类型为自身的可选类型对象
print(cat)                             // Optional(Animal(species: "Cat"))
let someAnimal = Animal(species: "")   // 传入空字符串会导致构造失败
print(someAnimal)                      // nil

enum Unit {                            // 枚举类型的可失败构造器
    case Alex, Bill, Carle
    init?(char: Character) {
        switch char {
        case "A":
            self = .Alex
        case "B":
            self = .Bill
        case "C":
            self = .Carle
        default:
            return nil
        }
    }
}
let unit1 = Unit(char:"B")
let unit2 = Unit(char:"D")

//:  必要构造器:子类必须实现的构造器
class SomeClass {
    required init() {                  // 添加required修饰符表示该类的子类都必须实现该构造器
        // 构造器内部实现
    }
}
class Subclass: SomeClass {            // 此时重写父类的指定构造器时,就不需要添加override修饰符
    required init() {                  // 子类也需要添加required,来应用于当前类的子类
        // 构造器内部实现
    }
}

//:  通过闭包或函数设置属性的默认值
class OtherClass {
    let someProperty: String = {
        return "someValue"             // 返回值得类型要和String一致
    }()                                // 后面加()表示立即执行此闭包,不加就相当于把闭包本身赋值给属性
}
let oClass = OtherClass()
print(oClass.someProperty)             // someValue



//:  析构过程: 当一个实例不再需要的时候,swift通过自动引用计数去自动清理内存的过程
// 析构器: 析构器只适用于类类型,类实例被释放前会自动调用析构器,析构器用关键字deinit来标识
class SomeClass1 {
    deinit {                            // 析构器只能定义在类中,且只能是一个
        // 执行析构过程                   // 子类继承的父类,也就继承了父类的析构器
    }                                   // deinit相当于OC的dealloc
}


//:  析构器实践
class DeinitTest {
    
    // 类中相关代码
    
    deinit {
        print("deinit")
    }
}
var test: DeinitTest? = DeinitTest()    // 定义一个可选类型,为了置为nil释放内存
test = nil                              // deinit, 实例没有引用,内存回收,调用deinit方法


//:  ARC(Auto Reference Counteting)自动引用计数:swift会通过ARC来自动管理内存.当类的实例不再被使用的时候,会自动释放其占用的内存
// 类和结构体是值类型,而引用计数只能应用于引用类型,比如说类
// 自动引用的工作机制:为了保证实例在使用的过程中不被销毁,ARC会自动计算一个实例被引用的次数,
// 只要引用次数不等于0,该实例都不会被销毁

class Person {                          // 自动引用计数实践
    let name: String
    init(name: String) {
        self.name = name
        print("\(name)正在被初始化")
    }
    deinit {
        print("\(name)即将被销毁")       // person3 = nil时打印
    }
}
var person1: Person?                    // 可选类型的变量,方便置空
var person2: Person?
var person3: Person?

person1 = Person(name: "Dariel")        // Person实例与person1建立了强引用
person2 = person1                       // 只要有一个强引用在,实例就能不被销毁
person3 = person1                       // 目前该实例共有三个强引用
person1 = nil
person2 = nil                           // 因为还有一个强引用,实例不会被销毁
person3 = nil                           // 最后一个强引用被断开,ARC会销毁它


//:  类实例之间的循环引用:两个实例互相持有对方的强引用
class People {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?           // 人住的公寓属性
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: People?                 // 公寓中的人的属性
    deinit {
        print("\(unit)被销毁")
    }
}
var people1:People? = People(name:"Dariel")
var apartmemt1:Apartment? = Apartment(unit:"4A")

people1?.apartment = apartmemt1
apartmemt1?.tenant = people1
people1 = nil
apartmemt1 = nil                        // 两个引用都置为nil了,但实例并没有销毁

//:  解决循环引用的三种方法
// 实例之间的循环强引用的解决办法:弱引用和无主引用.
// 对于在生命周期会变为nil的实例使用弱引用,
// 对于初始化后不能被赋值为nil的实例,使用无主引用.

// 弱引用:在声明属性前面加关键字weak
class OtherPeople {
    let name: String
    init(name: String) { self.name = name }
    var apartment: OtherApartment?      // 人住的公寓属性
    deinit { print("\(name)被销毁le") }
}

class OtherApartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: OtherPeople?       // 加一个weak关键字,表示该变量为弱引用
    deinit { print("\(unit)被销毁le") }
}
var otherPeople1: OtherPeople? = OtherPeople(name: "Dariel") // 定义两个实例变量
var otherApartment1: OtherApartment? = OtherApartment(unit: "4A")

otherPeople1!.apartment = otherApartment1 // 两者相互引用
otherApartment1?.tenant = otherPeople1  // 但tenant是弱引用
otherPeople1 = nil
otherApartment1 = nil                   // 实例被销毁,deinit中都会打印销毁的信息



// 无主引用:在声明属性前面加关键字unowned
class Dog {
    let name: String
    var food1: Food1?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name)被销毁") }
}
class Food1 {
    let number: Int
    unowned var owner: Dog              // owner是一个无主引用
    init(number: Int, owner: Dog) {
        self.number = number
        self.owner = owner
    }
    deinit { print("食物被销毁") }
}

var dog1: Dog? = Dog(name: "Kate")
dog1?.food1 = Food1(number: 6, owner: dog1!) // dog强引用food,而food对dog是无主引用

dog1 = nil                              // 这样就可以同时销毁两个实例了


// 如果互相引用的两个属性都为可选类型,也就是可以为nil,比较适合用弱引用来解决.如果两个互相引用的属性,
// 只有一个类的属性为可选类型,那么适合用无主引用来解决.那么,如果两个属性都不能是可选类型呢?
// 一个类使用无主属性,另一个类使用隐式解析可选属性.

class Country {
    let name: String
    // City后加!为隐式解析可选属性,类似可选类型,capitalCity属性的默认值为nil
    var capitalCity: City!              // 初始化完成后可以当非可选类型使用
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
    deinit { print("Country实例被销毁") }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
    deinit { print("City实例被销毁") }
}

// 这样一条语句就能够创建两个实例
var country: Country? = Country(name: "China", capitalName: "HangZhou")
print(country!.name)                    // China
print(country!.capitalCity.name)        // HangZhou
country = nil                           // 同时销毁两个实例


//:   闭包也是引用类型,也会引起的循环强引用
class Element {
    let name: String
    let text: String?
    
    lazy var group:() -> String = {     // 相当于一个没有参数返回string的函数
        [unowned self] in               // 定义捕获列表,将self变为无主引用
        if let text = self.text {       // 解包
            return "\(self.name), \(text)"
        }else {
            return "\(self.name)"
        }
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    deinit { print("\(name)被销毁") }
}

var element1: Element? = Element(name: "Alex", text: "Hello")
print(element1!.group())                // Alex, Hello,闭包与实例相互引用

element1 = nil                          // self为无主引用,实例可以被销毁





















